package top.ochiamalu.aioj.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.ochiamalu.aioj.common.ErrorCode;
import top.ochiamalu.aioj.constant.ModifyPhoneConstants;
import top.ochiamalu.aioj.converter.UserConverters;
import top.ochiamalu.aioj.converter.UserDetailConverters;
import top.ochiamalu.aioj.domain.dto.user.UpdateUserDetailDTO;
import top.ochiamalu.aioj.domain.dto.user.UserLoginDTO;
import top.ochiamalu.aioj.domain.dto.user.UserRegistrationDTO;
import top.ochiamalu.aioj.domain.entity.Email;
import top.ochiamalu.aioj.domain.entity.User;
import top.ochiamalu.aioj.domain.entity.UserDetail;
import top.ochiamalu.aioj.domain.vo.profile.EmailInfoVO;
import top.ochiamalu.aioj.domain.vo.profile.UserSecureInfoVO;
import top.ochiamalu.aioj.domain.vo.user.UpdateUserDetailVO;
import top.ochiamalu.aioj.domain.vo.user.UserInfoVO;
import top.ochiamalu.aioj.domain.vo.user.UserLoginVO;
import top.ochiamalu.aioj.enums.EmailEnum;
import top.ochiamalu.aioj.enums.ModifyPhoneEnum;
import top.ochiamalu.aioj.enums.UserEnum;
import top.ochiamalu.aioj.exception.BusinessException;
import top.ochiamalu.aioj.mapper.UserMapper;
import top.ochiamalu.aioj.properties.AIOJProperties;
import top.ochiamalu.aioj.service.EmailService;
import top.ochiamalu.aioj.service.UserDetailService;
import top.ochiamalu.aioj.service.UserService;
import top.ochiamalu.aioj.util.EmailUtils;
import top.ochiamalu.aioj.util.IP2RegionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

import static top.ochiamalu.aioj.constant.UserConstants.USER_ACCOUNT_MIN_LENGTH;

/**
 * 用户服务impl
 *
 * @author OchiaMalu
 * @date 2025/01/24
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    /**
     * 默认头像
     */
    private static final String DEFAULT_AVATAR = "https://niu.ochiamalu.top/a84b1306e46061c0d664e6067417e5b.jpg";

    private static final Gson GSON = new Gson();

    @Resource
    private AIOJProperties aiojProperties;

    @Resource
    private UserDetailService userDetailService;

    @Resource
    private EmailService emailService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 登录
     *
     * @param userLoginDTO 用户登录dto
     * @return {@link UserLoginVO }
     */
    @Override
    public UserLoginVO login(UserLoginDTO userLoginDTO) {
        if (userLoginDTO.getAccount().length() < USER_ACCOUNT_MIN_LENGTH) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
        }
        User user = lambdaQuery()
                .eq(User::getAccount, userLoginDTO.getAccount())
                .one();
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "账号不存在");
        }
        if (user.getRoleId().equals(UserEnum.BAN.getCode())) {
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "用户被封禁");
        }
        if (!DigestUtil.bcryptCheck(userLoginDTO.getPassword(), user.getPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
        }
        StpUtil.login(user.getId());

        handleUpdateUserIpAddress(user.getId(), "112.10.182.78");

        return UserConverters.INSTANCE.convertEntity2LoginVO(user);
    }

    /**
     * 注册
     *
     * @param userRegistrationDTO 用户注册数据
     * @return {@link UserLoginVO }
     */
    @Override
    public UserLoginVO register(UserRegistrationDTO userRegistrationDTO) {
        if (userRegistrationDTO.getAccount().length() < USER_ACCOUNT_MIN_LENGTH) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误");
        }
        if (!userRegistrationDTO.getEmail().matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱格式错误");
        }
        if (lambdaQuery().eq(User::getAccount, userRegistrationDTO.getAccount()).count() > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号已存在");
        }
        if (lambdaQuery().eq(User::getEmail, userRegistrationDTO.getEmail()).count() > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱已存在");
        }
        User user = User.builder().account(userRegistrationDTO.getAccount())
                .password(DigestUtil.bcrypt(userRegistrationDTO.getPassword()))
                .roleId(UserEnum.USER.getCode())
                .username(userRegistrationDTO.getAccount())
                .email(userRegistrationDTO.getEmail())
                .avatar(DEFAULT_AVATAR)
                .build();
        boolean saved = this.save(user);
        if (!saved) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败,系统错误");
        }
        StpUtil.login(user.getId());

        handleUpdateUserIpAddress(user.getId(), userRegistrationDTO.getAddress());

        return UserConverters.INSTANCE.convertEntity2LoginVO(user);
    }

    private void handleUpdateUserIpAddress(Long userId, String address) {
        UserDetail userDetail = new UserDetail();
        userDetail.setUserId(userId);
        String toAddress = IP2RegionUtils.changeIpToAddress(address);
        String[] split = toAddress.split("\\|");
        // 处理省份名称
        String province = split[2]
                .replaceAll("维吾尔自治区", "")
                .replaceAll("壮族自治区", "")
                .replaceAll("回族自治区", "")
                .replaceAll("自治区", "")
                .replaceAll("省", "")
                .replaceAll("市", "");
        String city = split[3].replaceAll("市", "").replaceAll("区", "");
        userDetail.setIpAddress(province + city);
        userDetailService.saveOrUpdate(userDetail, new LambdaQueryWrapper<UserDetail>().eq(UserDetail::getUserId, userId));
    }

    /**
     * 更新用户详细信息
     *
     * @param updateUserDetailDTO 更新用户详细信息dto
     * @return boolean
     */
    @Override
    @Transactional
    public boolean updateUserDetail(UpdateUserDetailDTO updateUserDetailDTO) {
        // 获取当前登录用户id
        Long loginUserId = StpUtil.getLoginIdAsLong();

        // 校验是否有权限修改
        Long targetUserId = updateUserDetailDTO.getUserId();
        if (!loginUserId.equals(targetUserId)) {
            // 检查是否是管理员
            String userRole = StpUtil.getSession().getString("role");
            if (!UserEnum.ADMIN.getCode().toString().equals(userRole)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权修改他人信息");
            }
        }

        // 更新用户基本信息
        User user = new User();
        user.setId(targetUserId);
        user.setUsername(updateUserDetailDTO.getUsername());
        user.setAvatar(updateUserDetailDTO.getAvatar());

        boolean updated = this.updateById(user);
        if (!updated) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新用户信息失败");
        }

        // 更新用户详细信息
        UserDetail userDetail = new UserDetail();
        userDetail.setUserId(targetUserId);
        userDetail.setGender(updateUserDetailDTO.getGender());
        userDetail.setBirthday(updateUserDetailDTO.getBirthday());
        userDetail.setCurrentResidence(updateUserDetailDTO.getCurrentResidence());
        userDetail.setProfile(updateUserDetailDTO.getProfile());
        userDetail.setWebsite(updateUserDetailDTO.getWebsite());
        userDetail.setGithub(updateUserDetailDTO.getGithub());
        userDetail.setSchool(updateUserDetailDTO.getSchool());
        userDetail.setCompany(updateUserDetailDTO.getCompany());
        userDetail.setPosition(updateUserDetailDTO.getPosition());

        // 转换并设置标签
        String tagStr = GSON.toJson(updateUserDetailDTO.getTagList());
        userDetail.setTags(tagStr);

        // 更新用户详细信息
        return userDetailService.saveOrUpdate(userDetail,
                new LambdaQueryWrapper<UserDetail>().eq(UserDetail::getUserId, targetUserId));
    }

    /**
     * 获取用户详细信息
     *
     * @return {@link UpdateUserDetailVO }
     */
    @Override
    public UpdateUserDetailVO getUserDetail() {
        // 获取当前登录用户id
        Long loginUserId = StpUtil.getLoginIdAsLong();

        User user = this.getById(loginUserId);

        UserDetail userDetail = userDetailService.getOne(new LambdaQueryWrapper<UserDetail>().eq(UserDetail::getUserId, loginUserId));
        if (userDetail == null) {
            return null;
        }
        UpdateUserDetailVO updateUserDetailVO = UserDetailConverters.INSTANCE.convertEntity2VO(userDetail);
        updateUserDetailVO.setUsername(user.getUsername());
        updateUserDetailVO.setAvatar(user.getAvatar());
        updateUserDetailVO.setTagList(GSON.fromJson(userDetail.getTags(), new TypeToken<List<String>>() {
        }.getType()));
        return updateUserDetailVO;
    }

    /**
     * 获取用户信息
     *
     * @param account 账户
     * @return {@link UserInfoVO }
     */
    @Override
    public UserInfoVO getUserInfo(String account) {
        User user = this.lambdaQuery().eq(User::getAccount, account).one();
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        UserInfoVO userInfoVO = new UserInfoVO();
        userInfoVO.setAccount(account);
        userInfoVO.setUsername(user.getUsername());
        userInfoVO.setAvatar(user.getAvatar());

        UserDetail detail = userDetailService.lambdaQuery().eq(UserDetail::getUserId, user.getId()).one();
        if (detail != null) {
            userInfoVO.setProfile(detail.getProfile());
            userInfoVO.setIpAddress(detail.getIpAddress());

            String currentResidence = detail.getCurrentResidence();
            if (currentResidence != null) {
                String[] split = currentResidence.split("/");
                // 处理省份名称
                String province = split[0]
                        .replaceAll("维吾尔自治区", "")
                        .replaceAll("壮族自治区", "")
                        .replaceAll("回族自治区", "")
                        .replaceAll("自治区", "")
                        .replaceAll("省", "")
                        .replaceAll("市", "");
                String city = split[1].replaceAll("市", "").replaceAll("区", "");

                userInfoVO.setCurrentResidence(province + city);
            }
            userInfoVO.setGender(detail.getGender());
            if (detail.getTags() != null) {
                userInfoVO.setTagList(GSON.fromJson(detail.getTags(), new TypeToken<List<String>>() {
                }.getType()));
            }
        }
        return userInfoVO;
    }

    /**
     * 通过帐户获取用户ID
     *
     * @param account 账户
     * @return {@link Long }
     */
    @Override
    public Long getUserIdByAccount(String account) {
        return this.lambdaQuery().eq(User::getAccount, account).one().getId();
    }

    /**
     * 获取用户安全信息
     *
     * @return {@link UserSecureInfoVO }
     */
    @Override
    public UserSecureInfoVO getUserSecureInfo() {
        long userId = StpUtil.getLoginIdAsLong();
        User user = this.getById(userId);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        UserSecureInfoVO userSecureInfoVO = new UserSecureInfoVO();
        userSecureInfoVO.setDisplayPhone(maskPhone(user.getPhone()));

        //获取邮箱
        List<Email> emailList = emailService.lambdaQuery().eq(Email::getUserId, userId).list();
        if (emailList != null && !emailList.isEmpty()) {
            List<EmailInfoVO> emailInfoVOList = emailList.stream().map(email -> {
                EmailInfoVO emailInfoVO = new EmailInfoVO();
                emailInfoVO.setId(email.getId());
                emailInfoVO.setEmail(maskEmail(email.getEmail()));
                emailInfoVO.setIsVerified(EmailEnum.TRUE.getCode().equals(email.getIsVerified()));
                emailInfoVO.setIsPrimary(EmailEnum.TRUE.getCode().equals(email.getIsPrimary()));

                return emailInfoVO;
            }).collect(Collectors.toList());
            userSecureInfoVO.setDisplayEmail(emailInfoVOList);
        }
        return userSecureInfoVO;
    }

    @Override
    public boolean addEmail(String email) {
        // 获取当前登录用户id
        Long userId = StpUtil.getLoginIdAsLong();

        // 验证邮箱格式
        if (!email.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "邮箱格式错误");
        }

        // 检查邮箱是否已被使用
        if (emailService.lambdaQuery().eq(Email::getEmail, email).count() > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该邮箱已被使用");
        }

        // 生成验证token
        String verificationToken = EmailUtils.generateVerificationToken(userId, email);

        // 创建新的邮箱记录
        Email newEmail = Email.builder()
                .userId(userId)
                .email(email)
                .verificationToken(verificationToken)
                .isVerified(EmailEnum.FALSE.getCode())
                .isPrimary(EmailEnum.FALSE.getCode())
                .build();

        // 保存邮箱记录
        boolean saved = emailService.save(newEmail);

        if (!saved) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "添加邮箱失败");
        }

        // 生成验证链接
        String verificationUrl = String.format(aiojProperties.getEmailVerificationUrl() + "%s", verificationToken);

        // 发送验证邮件
        emailService.sendVerificationEmail(email, verificationUrl);
        return true;
    }

    @Override
    public boolean modifyPhone(Integer type, String phone, String code) {
        if (!phone.matches("0?(13|14|15|18|17)[0-9]{9}")) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号格式错误");
        }

        ModifyPhoneEnum phoneEnum = ModifyPhoneEnum.getEnumByType(type);
        if (phoneEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "无效的操作类型");
        }

        long userId = StpUtil.getLoginIdAsLong();

        if (phoneEnum.getType().equals(ModifyPhoneEnum.UNBIND.getType())) {
            String rCode = stringRedisTemplate.opsForValue().get(ModifyPhoneConstants.UNBIND_PHONE_KEY + phone);
            if (rCode == null || !rCode.equals(code)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误");
            }
        }
        if (phoneEnum.getType().equals(ModifyPhoneEnum.BIND.getType())) {
            String rCode = stringRedisTemplate.opsForValue().get(ModifyPhoneConstants.BIND_PHONE_KEY + phone);
            if (rCode == null || !rCode.equals(code)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误");
            }
            if (this.lambdaQuery().eq(User::getPhone, phone).count() > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "该手机号已被绑定");
            }
            boolean updated = this.lambdaUpdate().eq(User::getId, userId)
                    .set(User::getPhone, phone)
                    .update();
            if (!updated) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "更新用户手机号失败");
            }
        }
        return true;
    }

    @Override
    public boolean updatePassword(String oldPassword, String newPassword) {
        if (StringUtils.isAnyBlank(oldPassword, newPassword)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入正确的信息");
        }
        long userId = StpUtil.getLoginIdAsLong();
        User user = this.getById(userId);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }
        if (!DigestUtil.bcryptCheck(oldPassword, user.getPassword())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "旧密码错误");
        }

        String encryptPassword = DigestUtil.bcrypt(newPassword);
        return this.lambdaUpdate().eq(User::getId, userId)
                .set(User::getPassword, encryptPassword)
                .update();
    }


    /**
     * 邮箱去敏感化
     *
     * @param email 电子邮件
     * @return {@link String }
     */
    private String maskEmail(String email) {
        if (email == null) {
            return null;
        }
        int atIndex = email.indexOf('@');
        if (atIndex == -1) {
            // 如果不是有效的邮箱格式，直接返回
            return email;
        }
        String localPart = email.substring(0, atIndex);
        String domainPart = email.substring(atIndex);
        if (localPart.length() <= 2) {
            // 如果本地部分太短，不进行掩码
            return localPart + domainPart;
        }
        return localPart.charAt(0) + StringUtils.repeat('*', localPart.length() - 2) + localPart.charAt(localPart.length() - 1) + domainPart;
    }

    /**
     * 手机号去敏感化
     *
     * @param phone 电话
     * @return {@link String }
     */
    private String maskPhone(String phone) {
        if (phone == null || phone.length() < 7) {
            // 如果电话号码为空或太短，不进行掩码
            return phone;
        }
       return phone.substring(0, 3) + StringUtils.repeat('*', phone.length() - 7) + phone.substring(phone.length() - 4);
    }
}
